/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

//初始化三种状态
const PENDING = 'pengding';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

/* 声明一个MyPromise */
class MyPromise {
    // 当前promise的状态
    status = PENDING;
    // 当前promise的值
    value = undefined;
    // 当前promise如果失败 失败的原因
    reason = undefined;
    // 如果异步 那么将回调存入到对应回调库中
    successCallbacks = [];
    rejectCallbacks = [];
    constructor(executor) {
            try {
                executor(this.resolve, this.reject);
            } catch (error) {
                this.reject(error);
            }
        }
        // 静态resolve接收一个参数
    static resolve(value) {
        if (value instanceof MyPromise) return value; // 如果这个参数是promise对象 那么直接返回 
        return new MyPromise(resolve => resolve(value)); // 如果不是 用promise包裹后返回
    }

    // 成功调用resolve方法 接收一个参数作为这个promise的返回值存在promise对象中
    resolve = data => {
            // 一旦status状态不是pending 那么就不再执行 保证状态不可逆
            if (this.status !== PENDING) return;
            this.status = FULFILLED;
            this.value = data; // 将promise成功获得的数据存在value中
            // 判断成功回调函数库中是否用未执行的函数 有的话依次执行
            while (this.successCallbacks.length) {
                this.successCallbacks.shift()();
            }
        }
        // 同resolve
    reject = reason => {
            if (this.status !== PENDING) return;
            this.status = REJECTED;
            this.reason = reason;
            while (this.rejectCallbacks.length) this.rejectCallbacks.shift()();
        }
        // then方法 接收两个函数作为参数 第一个是成功的回调
    then = (successCallback, rejectCallback) => {
            successCallback = successCallback || (value => value); // 如果没有传入回调 默认把当前值返回
            rejectCallback = rejectCallback || (reason => { throw reason }); // 默认抛出当前错误信息
            // then方法 返回的是一个新的promise对象 所以在执行then的时候 new一个新的promise
            // 同样的 这个新promise的执行器也会立即执行
            let promise2 = new MyPromise((resolve, reject) => {
                // 当执行到then方法时候 status已经是FULFILLED说明promise已经执行成功
                // 直接执行成功的回调 
                // 同时将成功回调的返回值传递给下一个promise的resolve方法
                setTimeout(() => { // settimeout 0 为的是在promise2中能够拿到promise2来判断是否返回本身
                    // 无论是成功还是失败的回调函数 都有可能执行出错 一旦出错了 会影响后续的then执行 所以用trycatch的方式
                    try { // try 里如果出错 那么直接调用下一个promise的reject
                        if (this.status === FULFILLED) {
                            const thenReturnVal = successCallback(this.value);
                            resolvePromise(promise2, thenReturnVal, resolve, reject);
                        } else if (this.status === REJECTED) {
                            const thenReturnVal = rejectCallback(this.reason);
                            resolvePromise(promise2, thenReturnVal, resolve, reject);
                        } else {
                            // 如果是异步的情况 必须判断每一个回调是否会执行出错 所有需要用trycatch包裹住回调函数
                            this.successCallbacks.push(() => {
                                try {
                                    const thenReturnVal = successCallback(this.value);
                                    resolvePromise(promise2, thenReturnVal, resolve, reject);
                                } catch (error) {
                                    reject(error);
                                }
                            });
                            this.rejectCallbacks.push(() => {
                                try {
                                    const thenReturnVal = rejectCallback(this.reason);
                                    resolvePromise(promise2, thenReturnVal, resolve, reject);
                                } catch (error) {
                                    reject(error);
                                }
                            })
                        }
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            });
            return promise2;
        }
        // all方法接收一个数组作为参数
    static all(array) {
            let count = 0;
            let results = []; // 声明一个结果数组 接收数组每一项的结果
            return new MyPromise((res, rej) => { // all方法返回一个新的promise对象
                function setData(index, value) { // 往results里面放值的函数
                    count++; // 没放入一个值 处理过的数量加1
                    results[index] = value; // results对应项设置好值
                    if (count === array.length) { // 如果处理过值的总数已经等于array的长度 说明所有的值都处理完了 
                        res(results); // 返回成功的结果
                    }
                }
                for (let i = 0; i < array.length; i++) {
                    if (array[i] instanceof MyPromise) { // 如果这一项是promise类型的
                        array[i].then((val) => { // 那么等promise执行完 在then里面拿到执行结果再往results数组里放
                            setData(i, val);
                        }, reason => {
                            rej(reason); // 如果有一项的promise出错了 直接rej
                        });
                    } else { // 如果是普通值 直接往数组里放
                        setData(i, array[i]);
                    }
                }
            })
        }
        //finally有两个特点1、无论当前的 Promise 对象状态是成功的还是失败的, finally 方法中传入的回调函数都会得到执行2、finally 方法的返回值是 Promise 对象, 所以在调用方法后还可以链式调用 then 方法
    finally = callback => {
            // finally 也返回一个新的promise 在这个新promise的then方法里可以拿到最后成功或失败的值
            return this.then(value => { // 利用then方法可以直到最后是成功了还是失败了 无论成功还是失败都要执行callback
                // 如果callback的返回值是一个异步的promise 利用resolve静态方法包裹这个返回值
                // 例如 如果callback回调返回了 p3 而p3是一个延迟2秒执行的promise
                // 那么MyPromise.resolve(p3) === p3 也就是 p3.then。。。这样finally就会等这个promise执行完 才会调用then方法
                return MyPromise.resolve(callback()).then(() => value);
            }, reason => {
                // 同样的 如果callback的promise是失败的 那么执行抛出这个reason
                return MyPromise.resolve(callback()).then(() => { throw reason });
            }); // 调用then方法获取当前promise的状态
        }
        // promise中一旦某一处有错误发生 会因为then的特性一直传递下去 直到最后这个catch里
    catch = rejectCallback => {
        return this.then(undefined, rejectCallback);
    }
}

function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
        reject(new TypeError('Chaning cycle deteced for promise #<Promise>'));
    }
    if (x instanceof MyPromise) {
        // 是promise对象 那需要调用Promise的then方法判断这个promise是否成功
        // 如果成功 调用resolve方法 并且把值传递下去 如果失败 调用reject并且把原因传递下去
        x.then(value => resolve(value), reason => reject(reason));
        // x.then(resolve, reject);
    } else {
        resolve(x);
    }
}

module.exports = MyPromise;